﻿// -----------------------------------------------------------------------
// <copyright file="FileSystem.cs" company="Company">
// Company GmbH (R) 2011
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;
using Company.Common.Control;
using Company.Common.Helper;

namespace Company.IO.Interfaces
{
    /// <summary>
    /// FileSystem Wrapper Base class
    /// </summary>
    [Guid("de61cbcc-085c-41a0-aca8-4784f3c2f7ff")]
    public abstract class FileSystemTemplate<TDirectory, TPath, TFile> : IFileSystem
        where TDirectory : IDirectory
        where TPath : IPath
        where TFile : IFile
    {
        /// <summary>
        /// Creates a new instance of the FileSystem class
        /// </summary>
        /// <param name="directory">The Directory to use</param>
        /// <param name="path">The Path to use</param>
        /// <param name="file">The File object to use</param>
        protected FileSystemTemplate(IDirectory directory, IPath path, IFile file)
        {
            Directory = directory;
            Path = path;
            File = file;
        }

        protected IDirectory Directory { get; private set; }
        protected IPath Path { get; private set; }
        protected IFile File { get; private set; }

        #region IFileSystem Members

        IDirectory IFileSystem.Directory
        {
            get { return Directory; }
        }

        IPath IFileSystem.Path
        {
            get { return Path; }
        }

        IFile IFileSystem.File
        {
            get { return File; }
        }

        #endregion

        #region IFileSystem Functions

        void IFileSystem.CopyDirectory(string sourcePath, string destinationPath)
        {
            IDirectoryInfo diSource = Directory.GetDirectoryInfo(sourcePath);
            IDirectoryInfo diTarget = Directory.GetDirectoryInfo(destinationPath);

            CopyAll(diSource, diTarget);
        }

        void IFileSystem.MoveFiles(string sourcePath, string destinationPath)
        {
            Process(sourcePath, destinationPath, Directory.Move, File.Move);
        }

        bool IFileSystem.DirectoryHasFiles(string directoryPath, bool recursive)
        {
            if (!Directory.Exists(directoryPath))
                return false;

            SearchOption searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

            return Directory.GetFiles(directoryPath, "*", searchOption).Length > 0;
        }

        #endregion

        protected abstract string NormalizeDirectory(string path);

        private void CopyAll(IDirectoryInfo source, IDirectoryInfo target)
        {
            if (source.FullName.ToLower() == target.FullName.ToLower())
            {
                return;
            }

            // Check if the target directory exists, if not, create it.
            if (Directory.Exists(target.FullName) == false)
            {
                Directory.CreateDirectory(target.FullName);
            }

            // Copy each file into it's new directory.
            Loop.ForEach(source.GetFiles(), fi => fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true));

            // Copy each subdirectory using recursion.
            Loop.ForEach(source.GetDirectories(), diSourceSubDir =>
                                                      {
                                                          IDirectoryInfo nextTargetSubDir =
                                                              target.CreateSubdirectory(diSourceSubDir.Name);
                                                          CopyAll(diSourceSubDir, nextTargetSubDir);
                                                      });
        }

        private void Process(string sourceBaseDirectory,
                             string targetBaseDirectory,
                             Action<string, string> directoryAction,
                             Action<string, string> fileAction)
        {
            CompanyContract.ThrowIfNull(sourceBaseDirectory, "sourceBaseDirectory is null.");
            CompanyContract.ThrowIfTrue(sourceBaseDirectory == string.Empty, "sourceBaseDirectory is empty.");

            CompanyContract.ThrowIfNull(targetBaseDirectory, "targetBaseDirectory is null.");
            CompanyContract.ThrowIfTrue(targetBaseDirectory == string.Empty, "targetBaseDirectory is empty.");

            CompanyContract.ThrowIfNull(directoryAction, "directoryAction is null.");
            CompanyContract.ThrowIfNull(fileAction, "fileAction is null.");

            sourceBaseDirectory = NormalizeDirectory(sourceBaseDirectory);
            targetBaseDirectory = NormalizeDirectory(targetBaseDirectory);

            Directory.CreateDirectory(targetBaseDirectory);

            Loop.ForEach(Directory.GetDirectories(sourceBaseDirectory, "*", SearchOption.TopDirectoryOnly),
                         sourceDirectory =>
                             {
                                 string targetDirectory = Path.Combine(sourceDirectory, targetBaseDirectory);
                                 directoryAction(sourceDirectory, targetDirectory);
                             });

            Loop.ForEach(Directory.EnumerateFiles(sourceBaseDirectory, "*", SearchOption.TopDirectoryOnly),
                         sourceFile =>
                             {
                                 string targetFile = Path.Combine(sourceFile, targetBaseDirectory);
                                 fileAction(sourceFile, targetFile);
                             });
        }

    }
}